import { useMemo, useRef, useCallback, useState } from 'react'
import PropTypes from "prop-types"
import { useDrag, useDrop, DndProvider } from "react-dnd"
import { HTML5Backend } from "react-dnd-html5-backend"
import { Button, Row, Col } from "antd"
import { CloseOutlined, DragOutlined } from '@ant-design/icons'
import inputMap from "./InputMiniMap"
import { generateRandomNumber } from '../../utils'

const InputArrayRow = ({
  index,
  id,
  handleMove,
  handleDelete,
  inputs,
  dragKey
}) => {

  const rowRef = useRef(null)

  const [{ handlerId }, drop] = useDrop({
    accept: dragKey,
    collect: (monitor) => ({
      handlerId: monitor.getHandlerId(),
    }),
    hover(item, monitor) {
      if (!rowRef.current) {
        return;
      }
      const dragIndex = item.index;
      const hoverIndex = index;
      if (dragIndex === hoverIndex) {
        return;
      }
      const hoverBoundingRect = rowRef.current?.getBoundingClientRect();
      const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;
      const clientOffset = monitor.getClientOffset();
      const hoverClientY = clientOffset.y - hoverBoundingRect.top;
      if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) {
        return;
      }
      if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) {
        return;
      }
      handleMove(dragIndex, hoverIndex);
      item.index = hoverIndex;
    }
  })

  const [{ isDragging }, drag, preview] = useDrag({
    type: dragKey,
    item: () => {
      return { id, index };
    },
    collect: (monitor) => ({
      isDragging: monitor.isDragging(),
    }),
  })
  const opacity = isDragging ? '0' : '1'

  drag(drop(rowRef))

  return <div
    style={{
      marginBottom: 10,
      display: 'flex',
      opacity
    }}
    data-handler-id={handlerId}
    ref={preview}
  >
    <div style={{ marginRight: 4 }}>
      <Button ref={rowRef} style={{ cursor: 'move' }}>
        <DragOutlined />
      </Button>
    </div>
    <div style={{ flex: 1, width: 0 }}> {inputs}</div>
    <div style={{ marginLeft: 4 }} >
      <Button
        type="primary"
        danger
        onClick={() => {
          handleDelete(index)
        }}
      >
        <CloseOutlined />
      </Button>
    </div>
  </div>
}

const InputArray = ({
  schema = [],
  getNewRowData,
  value = [],
  onChange
}) => {
  const [dragKey] = useState('DRAG_ARRAY_' + generateRandomNumber(12))

  const handleCreate = () => {
    let newRowData = {}
    if (!!getNewRowData) {
      newRowData = {
        ...getNewRowData(value),
        $$hashKey: generateRandomNumber(6)
      }
    } else {
      schema.forEach(el => {
        newRowData.$$hashKey = generateRandomNumber(6)
        newRowData[el.name] = undefined
      })
    }
    onChange(value.concat([newRowData]))
  }

  const handleDelete = (index) => {
    onChange(value.filter((el, i) => i !== index))
  }

  const handleModify = ({ index, key, val }) => {
    onChange(value.map((el, i) => i === index ? {
      ...el,
      [key]: val
    } : el))
  }

  const handleMove = useCallback((dragIndex, hoverIndex) => {
    onChange(value.map((el, i) => {
      if (i === dragIndex) {
        return value[hoverIndex]
      } else if (i === hoverIndex) {
        return value[dragIndex]
      } else {
        return el
      }
    }))
  }, [value])

  const colSpanObj = useMemo(() => {
    let res = {}
    // 剩余为分配的span数
    const surplusSpans = 24 - schema.filter(el => !!el.span).reduce((total, el) => total + el.span, 0)
    // 未分配的项
    const notSetSpanCols = schema.filter(el => !el.span)
    // 最后一个为分配项
    const lastNotSetSpanCol = notSetSpanCols[notSetSpanCols.length - 1]
    // 未分配项的默认宽度
    const defaultSpan = (surplusSpans - (surplusSpans % notSetSpanCols.length)) / notSetSpanCols.length

    const getColSpan = (item) => {
      let result
      if (item.span) {
        result = item.span
      } else {
        if (item.name === lastNotSetSpanCol.name) {
          result = surplusSpans - defaultSpan * (notSetSpanCols.length - 1)
        } else {
          result = defaultSpan
        }
      }
      return result
    }

    schema.forEach(item => {
      res[item.name] = getColSpan(item)
    })

    return res
  }, [schema])


  const renderInput = (formItem, props) => {
    const inputTypes = Object.keys(inputMap)

    let type = 'text'

    if (formItem.type) {
      if (typeof formItem.type === 'string') {
        if (inputTypes.includes(formItem.type)) {
          type = formItem.type
        }
      } else if (typeof formItem.type === 'function') {
        type = formItem.type(form)
      }
    }

    const FormInputName = inputMap[type].component
    const inputProps = formItem.inputProps || {}

    return <FormInputName
      {...inputProps}
      {...props}
    ></FormInputName>
  }

  const renderFields = (el, i) => {
    return <Row gutter={[4, 4]} style={{ width: '100%' }}>
      {
        schema.map((item, index) =>
          <Col
            key={index}
            span={colSpanObj[item.name]}
          >
            {
              renderInput(
                item,
                {
                  value: el[item.name],
                  label: item.label,
                  placeholder: item.label,
                  title: item.label,
                  onChange: (value) => {
                    handleModify({
                      index: i,
                      key: item.name,
                      val: value
                    })
                  }
                }
              )
            }
          </Col>
        )
      }
    </Row>
  }

  const valueWithId = useMemo(() => {
    return (value && value.length) ? value.map(el => {
      if (!el.$$hashKey) {
        return {
          ...el,
          $$hashKey: generateRandomNumber(6)
        }
      } else {
        return el
      }
    }) : []
  }, [value])

  return <DndProvider backend={HTML5Backend}>
    <div>
      <div>
        {
          valueWithId.length > 0 ?
            valueWithId.map((el, i) => <InputArrayRow
              dragKey={dragKey}
              key={el.$$hashKey}
              id={el.$$hashKey}
              index={i}
              handleDelete={handleDelete}
              handleMove={handleMove}
              inputs={renderFields(el, i)}
            ></InputArrayRow>) :
            <div style={{ color: '#999', height: 34, lineHeight: '24px' }}>暂无数据</div>
        }
      </div>
      <div>
        <Button size="small" type="primary" onClick={handleCreate}>添加一项</Button>
      </div>
    </div>
  </DndProvider>
}

InputArray.propTypes = {
  schema: PropTypes.array.isRequired,
  getNewRowData: PropTypes.func,
  value: PropTypes.array,
  onChange: PropTypes.func
}

InputArray.propsMap = []

export default InputArray